package com.anjiplus.template.gaea.auth.modules.user.service.impl;

import com.anji.plus.gaea.bean.KeyValue;
import com.anji.plus.gaea.bean.TreeNode;
import com.anji.plus.gaea.cache.CacheHelper;
import com.anji.plus.gaea.constant.BaseOperationEnum;
import com.anji.plus.gaea.constant.Enabled;
import com.anji.plus.gaea.constant.GaeaConstant;
import com.anji.plus.gaea.constant.GaeaKeyConstant;
import com.anji.plus.gaea.curd.mapper.GaeaBaseMapper;
import com.anji.plus.gaea.exception.BusinessException;
import com.anji.plus.gaea.exception.BusinessExceptionBuilder;
import com.anji.plus.gaea.holder.UserContentHolder;
import com.anji.plus.gaea.security.utils.SecurityUtils;
import com.anji.plus.gaea.utils.GaeaBeanUtils;
import com.anji.plus.gaea.utils.GaeaUtils;
import com.anjiplus.template.gaea.auth.modules.menu.controller.dto.TreeDTO;
import com.anjiplus.template.gaea.auth.modules.org.dao.GaeaOrgMapper;
import com.anjiplus.template.gaea.auth.modules.org.dao.entity.GaeaOrg;
import com.anjiplus.template.gaea.auth.modules.role.controller.dto.RoleOrgDto;
import com.anjiplus.template.gaea.auth.modules.role.dao.GaeaRoleOrgMapper;
import com.anjiplus.template.gaea.auth.modules.user.controller.dto.GaeaUserQueryDto;
import com.anjiplus.template.gaea.auth.modules.user.controller.param.GaeaUserPasswordParam;
import com.anjiplus.template.gaea.auth.modules.user.controller.param.GaeaUserQueryParam;
import com.anjiplus.template.gaea.auth.modules.user.controller.param.UserRoleOrgReqParam;
import com.anjiplus.template.gaea.auth.modules.user.dao.GaeaUserMapper;
import com.anjiplus.template.gaea.auth.modules.user.dao.GaeaUserRoleOrgMapper;
import com.anjiplus.template.gaea.auth.modules.user.dao.entity.GaeaUser;
import com.anjiplus.template.gaea.auth.modules.user.dao.entity.GaeaUserRoleOrg;
import com.anjiplus.template.gaea.auth.modules.user.service.GaeaUserService;
import com.anjiplus.template.gaea.common.MagicValueConstants;
import com.anjiplus.template.gaea.common.RespCommonCode;
import com.anjiplus.template.gaea.common.aop.GaeaQuery;
import com.anjiplus.template.gaea.common.util.Md5Util;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 用户表(GaeaUser)ServiceImpl
 *
 * @author lr
 * @since 2021-02-02 13:38:12
 */
@Service
public class GaeaUserServiceImpl implements GaeaUserService {
    @Autowired
    private GaeaUserMapper gaeaUserMapper;
    @Autowired
    private GaeaUserRoleOrgMapper gaeaUserRoleOrgMapper;
    @Autowired
    private GaeaRoleOrgMapper gaeaRoleOrgMapper;
    @Autowired
    private GaeaOrgMapper gaeaOrgMapper;

    @Autowired
    private CacheHelper cacheHelper;


    @Override
    public GaeaBaseMapper<GaeaUser> getMapper() {
        return gaeaUserMapper;
    }

    @Override
    public GaeaUser getUserByUsername(String username) {
        LambdaQueryWrapper<GaeaUser> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(GaeaUser::getUsername, username);
        return gaeaUserMapper.selectOne(queryWrapper);
    }

    @Override
    public TreeDTO queryRoleTree(String username) {
        //该用户已经关联的组织角色
        LambdaQueryWrapper<GaeaUserRoleOrg> userRoleOrgWrapper = Wrappers.lambdaQuery();
        userRoleOrgWrapper.select(GaeaUserRoleOrg::getRoleCode, GaeaUserRoleOrg::getOrgCode).eq(GaeaUserRoleOrg::getUsername, username);
        //组装出选中的id
        List<GaeaUserRoleOrg> userRoleOrgList = gaeaUserRoleOrgMapper.selectList(userRoleOrgWrapper);
        List<String> checkedIds = userRoleOrgList.stream()
                .map(userRoleOrgPO -> String.format("%s_%s", userRoleOrgPO.getOrgCode(), userRoleOrgPO.getRoleCode()))
                .collect(Collectors.toList());
        //所有的角色组织
        List<RoleOrgDto> roleOrgDtos = gaeaRoleOrgMapper.queryAllRoleOrg();
        //所有的组织
        LambdaQueryWrapper<GaeaOrg> queryOrgWrapper = Wrappers.lambdaQuery();
        queryOrgWrapper.select(GaeaOrg::getId, GaeaOrg::getOrgCode, GaeaOrg::getOrgName, GaeaOrg::getOrgParentCode)
                .eq(GaeaOrg::getEnabled, Enabled.YES.getValue());
        List<GaeaOrg> orgList = gaeaOrgMapper.selectList(queryOrgWrapper);
        List<TreeNode> tree = buildOrgRoleTree(orgList, roleOrgDtos, "0");
        TreeDTO resultData = new TreeDTO();
        resultData.setTreeDatas(tree);
        resultData.setCheckedCodes(checkedIds);
        return resultData;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean saveRoleTree(UserRoleOrgReqParam reqParam) {
        //获取当前用户所拥有的角色，他只能管理这些角色，也就只能删除这些角色
        String roleOperationUser = UserContentHolder.getUsername();
        LambdaQueryWrapper<GaeaUserRoleOrg> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(GaeaUserRoleOrg::getUsername, roleOperationUser);
        List<GaeaUserRoleOrg> gaeaUserRoleOrgs = gaeaUserRoleOrgMapper.selectList(wrapper);
        //判断是否有超级管理员角色
        Optional<GaeaUserRoleOrg> exist = gaeaUserRoleOrgs.stream().filter(gaeaUserRoleOrg -> GaeaConstant.SUPER_ADMIN_ROLE.equals(gaeaUserRoleOrg.getRoleCode()))
                .findFirst();

        String username = reqParam.getUsername().toLowerCase();
        List<String> orgRoleCodes = reqParam.getRoleOrgCodes();

        List<String> paramOrgs = orgRoleCodes.stream().map(item -> item.split(GaeaConstant.REDIS_SPLIT)[1])
                .collect(Collectors.toList());

        //该用户已经关联的组织角色
        LambdaQueryWrapper<GaeaUserRoleOrg> userRoleOrgWrapper = Wrappers.lambdaQuery();
        userRoleOrgWrapper.eq(GaeaUserRoleOrg::getUsername, username);

        //没有超级管理员
        if (!exist.isPresent()) {
            Map<String, List<String>> hasOrgRole = gaeaUserRoleOrgs.stream()
                    .collect(Collectors.groupingBy(GaeaUserRoleOrg::getOrgCode, Collectors.mapping(GaeaUserRoleOrg::getRoleCode, Collectors.toList())));
            paramOrgs.stream().forEach(orgCode -> {
                List<String> orgHashRoles = hasOrgRole.get(orgCode);
                userRoleOrgWrapper.eq(GaeaUserRoleOrg::getOrgCode, orgCode);
                if (!orgHashRoles.contains(GaeaConstant.SUPER_USER_NAME)) {
                    userRoleOrgWrapper.in(GaeaUserRoleOrg::getRoleCode, orgHashRoles);
                }
                gaeaUserRoleOrgMapper.delete(userRoleOrgWrapper);
            });

        } else {
            userRoleOrgWrapper.in(GaeaUserRoleOrg::getOrgCode, paramOrgs);
            gaeaUserRoleOrgMapper.delete(userRoleOrgWrapper);
        }

        //删除缓存
        String key = GaeaKeyConstant.USER_ROLE_SET_PREFIX + username;
        cacheHelper.delete(key);

        if (CollectionUtils.isEmpty(orgRoleCodes)) {
            return true;
        }
        List<GaeaUserRoleOrg> list = new ArrayList<>();


        orgRoleCodes.stream()
                .filter(codeStr -> StringUtils.isNotBlank(codeStr) && codeStr.contains(GaeaConstant.REDIS_SPLIT))
                .forEach(codesStr -> {
            GaeaUserRoleOrg userRoleOrg = new GaeaUserRoleOrg();
            String[] info = codesStr.split(GaeaConstant.REDIS_SPLIT);
            userRoleOrg.setOrgCode(info[0]);
            userRoleOrg.setRoleCode(info[1]);
            userRoleOrg.setUsername(username);
            list.add(userRoleOrg);
        });
        gaeaUserRoleOrgMapper.insertBatch(list);

        //刷新用户、机构、角色GaeaKeyConstant.USER_ROLE_SET_PREFIX + : + orgCode => map
        Map<String, String> userOrgRoleMap = list.stream()
                .collect(Collectors.groupingBy(GaeaUserRoleOrg::getOrgCode,
                        Collectors.mapping(GaeaUserRoleOrg::getRoleCode, Collectors.joining(GaeaConstant.SPLIT))));


        cacheHelper.hashSet(key, userOrgRoleMap);
        return true;
    }

    @Override
    public Boolean updatePassword(GaeaUserPasswordParam requestParam) {
        //参数校验
        if (!requestParam.getConfirmPassword().equals(requestParam.getPassword())) {
            //密码和确认密码不一致
            throw BusinessExceptionBuilder.build(RespCommonCode.AUTH_PASSWORD_NOTSAME);
        }
        //新密码不能与老密码一样
        if (StringUtils.equals(requestParam.getOldPassword(), requestParam.getPassword())) {
            throw BusinessExceptionBuilder.build(RespCommonCode.USER_PASSWORD_CONFIG_PASSWORD_CANOT_EQUAL);
        }
        String md5Pwd = Md5Util.encryptBySalt(requestParam.getPassword());
        String password = SecurityUtils.getPassword(md5Pwd);

        String username = UserContentHolder.getContext().getUsername();

        LambdaQueryWrapper<GaeaUser> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.select(GaeaUser::getId, GaeaUser::getPassword)
                .eq(GaeaUser::getUsername, username);
        GaeaUser gaeaUser = gaeaUserMapper.selectOne(queryWrapper);

        if (!SecurityUtils.matches(requestParam.getOldPassword(), gaeaUser.getPassword())) {
            throw BusinessExceptionBuilder.build(RespCommonCode.OLD_PASSWORD_ERROR);
        }
        gaeaUser.setPassword(password);
        int flag = gaeaUserMapper.updateById(gaeaUser);
        //返回结果
        return flag > 0;
    }


    @Override
    public List<GaeaOrg> getOrgByUsername(String username) {
        return gaeaUserRoleOrgMapper.getOrgInfoByUsername(username);
    }

    @Override
    public List<String> getRoleByUserOrg(String username, String orgCode) {
        LambdaQueryWrapper<GaeaUserRoleOrg> wrapper = Wrappers.lambdaQuery();
        wrapper.select(GaeaUserRoleOrg::getRoleCode,GaeaUserRoleOrg::getId)
                .eq(GaeaUserRoleOrg::getUsername,username)
                .and(StringUtils.isNotBlank(orgCode),e->e.eq(GaeaUserRoleOrg::getOrgCode, orgCode));
        List<GaeaUserRoleOrg> gaeaUserRoleOrgList = gaeaUserRoleOrgMapper.selectList(wrapper);
        return gaeaUserRoleOrgList.stream().map(GaeaUserRoleOrg::getRoleCode).collect(Collectors.toList());
    }

    /**
     * 组织角色树
     *
     * @param orgList
     * @param pid
     * @return
     */
    private static List<TreeNode> buildOrgRoleTree(List<GaeaOrg> orgList, List<RoleOrgDto> roleOrgVOS, Object pid) {
        List<TreeNode> childList = new ArrayList<>();
        orgList.forEach(orgPO -> {
            if (StringUtils.isNotEmpty(orgPO.getOrgParentCode()) && orgPO.getOrgParentCode().equals(pid)) {
                TreeNode treeVO = new TreeNode();
                treeVO.setId(orgPO.getOrgCode());
                treeVO.setLabel(orgPO.getOrgName());
                childList.add(treeVO);
            }
        });
        childList.forEach(treeVO -> {
            List<TreeNode> treeList = buildOrgRoleTree(orgList, roleOrgVOS, treeVO.getId());
            List<TreeNode> collect = roleOrgVOS.stream().filter(roleOrgVO -> roleOrgVO.getOrgCode().equals(treeVO.getId()))
                    .map(roleOrgVO -> {
                        TreeNode treeEntity = new TreeNode();
                        String roleOrgTreeId = String.format("%s_%s", treeVO.getId(), roleOrgVO.getRoleCode());
                        treeEntity.setId(roleOrgTreeId);
                        treeEntity.setLabel(roleOrgVO.getRoleName());
                        return treeEntity;
                    }).collect(Collectors.toList());
            if (!treeList.isEmpty()) {
                collect.addAll(treeList);
            }
            treeVO.setChildren(collect);
        });
        return childList;
    }

    /**
     * 新增用户，需要设置默认密码
     * @param entity        前端传递的对象
     * @param operationEnum 操作类型
     * @throws BusinessException
     */
    @Override
    public void processBeforeOperation(GaeaUser entity, BaseOperationEnum operationEnum) throws BusinessException {
        switch (operationEnum) {
            case INSERT:
                setDefault(entity);
                break;
            default:
        }
    }

    @Override
    public void processAfterOperation(GaeaUser entity, BaseOperationEnum operationEnum) throws BusinessException {
        String key = GaeaUtils.replaceFormatString(GaeaKeyConstant.USER_NICKNAME_KEY, UserContentHolder.getContext().getParams());
        String uuid = UserContentHolder.getUuid();
        switch (operationEnum) {
            case INSERT:
            case UPDATE:
                cacheHelper.hashSet(key, entity.getUsername(), entity.getNickname());
                if (Enabled.NO.getValue().equals(entity.getEnabled())) {
                    //清空登录状态
                    cacheHelper.delete(GaeaKeyConstant.USER_LOGIN_TOKEN + entity.getUsername() + GaeaConstant.REDIS_SPLIT + uuid);
                }
                break;
            case DELETE:
                cacheHelper.hashDel(key, entity.getUsername());
                cacheHelper.delete(GaeaKeyConstant.USER_LOGIN_TOKEN + entity.getUsername() + GaeaConstant.REDIS_SPLIT + uuid);
                break;
            default:
        }
    }


    @Override
    public void processBatchAfterOperation(List<GaeaUser> entities, BaseOperationEnum operationEnum) throws BusinessException {
        String uuid = UserContentHolder.getUuid();
        List<String> loginTokenKeys = entities.stream()
                .map(gaeaUser -> GaeaKeyConstant.USER_LOGIN_TOKEN + gaeaUser.getUsername() + GaeaConstant.REDIS_SPLIT + uuid)
                .collect(Collectors.toList());
        switch (operationEnum) {
            case DELETE_BATCH:
                //清空登录状态
                cacheHelper.delete(loginTokenKeys);

                Set<String> usernameSet = entities.stream().map(GaeaUser::getUsername).collect(Collectors.toSet());
                String key = GaeaUtils.replaceFormatString(GaeaKeyConstant.USER_NICKNAME_KEY, UserContentHolder.getContext().getParams());
                cacheHelper.hashBatchDel(key, usernameSet);
                break;
            default:
        }

    }

    /**
     * 设置默认值
     * @param entity
     */
    private void setDefault(GaeaUser entity) {
        String md5Pwd = Md5Util.encryptBySalt(MagicValueConstants.DEFAULT_PASSWORD);
        String defaultPwd = SecurityUtils.getPassword(md5Pwd);
        entity.setPassword(defaultPwd);
        entity.setAccountNonExpired(Enabled.YES.getValue());
        entity.setCredentialsNonExpired(Enabled.YES.getValue());
        entity.setAccountLocked(Enabled.NO.getValue());
    }

    @Override
    public void refreshCache(List<String> usernameList) {

        LambdaQueryWrapper<GaeaUser> wrapper = Wrappers.lambdaQuery();

        if (!CollectionUtils.isEmpty(usernameList)) {
            wrapper.in(GaeaUser::getUsername, usernameList);
        }

        List<GaeaUser> gaeaUsers = gaeaUserMapper.selectList(wrapper);

        Map<String, String> map = gaeaUsers.stream().collect(Collectors.toMap(GaeaUser::getUsername, GaeaUser::getNickname, (v1, v2) -> v2));

        String key = GaeaUtils.replaceFormatString(GaeaKeyConstant.USER_NICKNAME_KEY, UserContentHolder.getContext().getParams());
        cacheHelper.hashBatchDel(key, map.keySet());
        cacheHelper.hashSet(key, map);
    }

    @Override
    public List<String> getOrgCodes(String username) {
        LambdaQueryWrapper<GaeaUserRoleOrg> wrapper = Wrappers.lambdaQuery();
        wrapper.select(GaeaUserRoleOrg::getOrgCode);
        wrapper.eq(GaeaUserRoleOrg::getUsername, username);
        return gaeaUserRoleOrgMapper.selectList(wrapper).stream()
                .map(GaeaUserRoleOrg::getOrgCode).distinct()
                .collect(Collectors.toList());
    }

    /**
     * 用户信息高级查询
     * 高级查询在实现类中使用注解@GaeaQuery，方法后面的参数新增一个QueryWrapper动态参数，
     * 方法内部可以直接使用QueryWrapper
     * @param param
     * @param wrappers
     * @return
     */
    @Override
    @GaeaQuery
    public Page<GaeaUserQueryDto> queryAdvanceUserInfo(GaeaUserQueryParam param, QueryWrapper... wrappers) {
        Page<GaeaUserQueryDto> page = new Page<>(param.getPageNumber(), param.getPageSize());
        QueryWrapper queryWrapper = (null != wrappers && wrappers.length > 0) ? wrappers[0] : null;
        List<GaeaUserQueryDto> list= gaeaUserMapper.queryUserAdvance(page,param,queryWrapper);
        List<GaeaUserQueryDto> formatList=list.stream().map(e->{
            GaeaUserQueryDto queryDto=new GaeaUserQueryDto();
            GaeaBeanUtils.copyAndFormatter(e,queryDto);
            return queryDto;
        }).collect(Collectors.toList());
        page.setRecords(formatList);
        return page;
    }

    @Override
    public List<String> getOrgRoleMappings(String username) {
        LambdaQueryWrapper<GaeaUserRoleOrg> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(GaeaUserRoleOrg::getUsername, username);
        return gaeaUserRoleOrgMapper.selectList(wrapper)
                .stream()
                .map(gaeaUserRoleOrg -> gaeaUserRoleOrg.getOrgCode() + GaeaConstant.REDIS_SPLIT + gaeaUserRoleOrg.getRoleCode())
                .collect(Collectors.toList());
    }

    @Override
    public List<KeyValue> userSelect() {
        String key = GaeaUtils.replaceFormatString(GaeaKeyConstant.USER_NICKNAME_KEY, UserContentHolder.getContext().getParams());
        Map<String, String> userMap = cacheHelper.hashGet(key);

        List<KeyValue> userSelect = userMap.entrySet().stream().map(entry -> {
            KeyValue keyValue = new KeyValue();
            keyValue.setId(entry.getKey());
            keyValue.setText(entry.getValue());
            return keyValue;
        }).collect(Collectors.toList());

        return userSelect;
    }


    @Override
    public void setDefaultPwd(List<String> usernameList) {
        LambdaQueryWrapper<GaeaUser> wrapper = Wrappers.lambdaQuery();
        wrapper.in(GaeaUser::getUsername, usernameList);
        List<GaeaUser> gaeaUsers = list(wrapper);

        String md5Pwd = Md5Util.encryptBySalt(MagicValueConstants.DEFAULT_PASSWORD);
        String defaultPwd = SecurityUtils.getPassword(md5Pwd);
        Map<String,Object> map = new HashMap<>();
        map.put("password", defaultPwd);
        map.put("account_locked", Enabled.NO.getValue());
        map.put("account_non_expired", Enabled.YES.getValue());
        map.put("credentials_non_expired", Enabled.YES.getValue());
        map.put("password_update_time", new Date());
        gaeaUserMapper.updateFieldsBatch(map, gaeaUsers);
    }

    @Override
    public void unLock(List<String> usernameList) {

        LambdaQueryWrapper<GaeaUser> wrapper = Wrappers.lambdaQuery();
        wrapper.in(GaeaUser::getUsername, usernameList);
        List<GaeaUser> gaeaUsers = list(wrapper);

        Map<String,Object> map = new HashMap<>();
        map.put("account_locked", Enabled.NO.getValue());
        gaeaUserMapper.updateFieldsBatch(map, gaeaUsers);
    }

    @Override
    public Map<String,String> getUserOrgRoleMap(String username) {

        LambdaQueryWrapper<GaeaUserRoleOrg> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(GaeaUserRoleOrg::getUsername, username);

        List<GaeaUserRoleOrg> gaeaUserRoleOrgList = gaeaUserRoleOrgMapper.selectList(wrapper);

        return gaeaUserRoleOrgList.stream().collect(Collectors
                .groupingBy(GaeaUserRoleOrg::getOrgCode,
                        Collectors.mapping(GaeaUserRoleOrg::getRoleCode, Collectors.joining(GaeaConstant.SPLIT))));
    }

    @Override
    public List<GaeaUser> getUserBynams(List<String> usernames) {
        LambdaQueryWrapper<GaeaUser> wrapper = Wrappers.lambdaQuery();
        wrapper.select(GaeaUser::getId,GaeaUser::getUsername,GaeaUser::getDeviceId)
                .in(GaeaUser::getUsername,usernames);
        return gaeaUserMapper.selectList(wrapper);
    }
}
